#ifdef __aarch64__

.text
.align 5
.global IndirectGemmInt8to32_8x4
#ifndef __APPLE__
.type IndirectGemmInt8to32_8x4, %function
#endif

// void IndirectGemmInt8to32_8x4_dp(int8_t *output, int8_t *input, int8_t *weight, int32_t *bias,
//     size_t kSize, size_t ic8, size_t oc4, size_t offset, int min, int max);
// x0: output, x1: input, x2: weight, x3: bias, x4: kSize, x5: ic4, x6: oc4, x7: offset
// x8: min, x9: max
// BIAS IS TO SUBSTRACT, NOT TO ADD HERE
// we use sdot intrinsic on cores that supports dotprod(Armv8.2-A w/dp or later)
// mrs intrinsic could read system register ID_AA64ISAR0_EL1(or s3_0_c0_c6_0 on Armv8.2-A)
// the 44-48 bits indicates whether dotprod is supported
IndirectGemmInt8to32_8x4:

    .macro INIT_BIAS
        dup v24.4s, wzr
        dup v25.4s, wzr
        dup v26.4s, wzr
        dup v27.4s, wzr
        dup v28.4s, wzr
        dup v29.4s, wzr
        dup v30.4s, wzr
        dup v31.4s, wzr
    .endm

    // registers v8 ~ v15 must be preserved by a callee across subroutine calls, according to
    // https://github.com/ARM-software/abi-aa/blob/master/aapcs64/aapcs64.rst#simd-and-floating-point-registers
    // r19 ~ r29 should be also preserved
    // whereas our coding style do not permit such amount of parameters

    ldr w8, [sp, #0]
    ldr w9, [sp, #8]

    mul x5, x4, x5
    mov x4, #1

    LoopOc:

        mov x10, x4
        mov x12, x1

        LoopKsize:

            mov x11, x0
            INIT_BIAS

            // as some processors do not support sdot intrinsic, we use instruction word
            // dp support is stilled judged dymaticly, instruction word is just used to ensure compilation
            // according to https://static.docs.arm.com/ddi0596/g/ISA_A64_xml_v86A-2020-03_OPT.pdf
            // the instruction word of sdot vd.4s, vn.16b, vm.4b[index] is
            // 0100 1111 10Lm mmmm 1110 H0nn nnnd dddd
            // mmmmm/nnnnn/ddddd is the number of neon register, HL is the high/low bit of index

            // load input for output 1-8
            ld1r {v0.2d}, [x12], #8
            ld1r {v1.2d}, [x12], #8
            // load weight
            ld1 {v4.16b}, [x2], #16
            // step for output 1-4
            smull v8.8h, v4.8b, v0.8b
            smull v12.8h, v4.8b, v1.8b
            ld1 {v5.16b}, [x2], #16
            smull2 v9.8h, v4.16b, v0.16b
            smull2 v13.8h, v4.16b, v1.16b
            ld1r {v2.2d}, [x12], #8
            ld1r {v3.2d}, [x12], #8
            smull v10.8h, v5.8b, v0.8b
            smull v14.8h, v5.8b, v1.8b
            smull2 v11.8h, v5.16b, v0.16b
            smull2 v15.8h, v5.16b, v1.16b
            saddlp v16.4s, v8.8h
            saddlp v17.4s, v9.8h
            saddlp v18.4s, v10.8h
            saddlp v19.4s, v11.8h
            saddlp v20.4s, v12.8h
            saddlp v21.4s, v13.8h
            saddlp v22.4s, v14.8h
            saddlp v23.4s, v15.8h

            subs x13, x5, #1
            beq LoopIcEnd

            LoopIc:
                ld1r {v0.2d}, [x12], #8
                ld1r {v1.2d}, [x12], #8
                smull v8.8h, v4.8b, v2.8b
                smull v12.8h, v4.8b, v3.8b
                smull2 v9.8h, v4.16b, v2.16b
                smull2 v13.8h, v4.16b, v3.16b
                ld1 {v4.16b}, [x2], #16
                smull v10.8h, v5.8b, v2.8b
                smull v14.8h, v5.8b, v3.8b
                smull2 v11.8h, v5.16b, v2.16b
                smull2 v15.8h, v5.16b, v3.16b
                ld1 {v5.16b}, [x2], #16
                sadalp v24.4s, v8.8h
                sadalp v25.4s, v9.8h
                sadalp v26.4s, v10.8h
                sadalp v27.4s, v11.8h
                sadalp v28.4s, v12.8h
                sadalp v29.4s, v13.8h
                sadalp v30.4s, v14.8h
                sadalp v31.4s, v15.8h
                ld1r {v2.2d}, [x12], #8
                ld1r {v3.2d}, [x12], #8
                smull v8.8h, v4.8b, v0.8b
                smull v12.8h, v4.8b, v1.8b
                smull2 v9.8h, v4.16b, v0.16b
                smull2 v13.8h, v4.16b, v1.16b
                smull v10.8h, v5.8b, v0.8b
                smull v14.8h, v5.8b, v1.8b
                smull2 v11.8h, v5.16b, v0.16b
                smull2 v15.8h, v5.16b, v1.16b
                sadalp v16.4s, v8.8h
                sadalp v17.4s, v9.8h
                sadalp v18.4s, v10.8h
                sadalp v19.4s, v11.8h
                sadalp v20.4s, v12.8h
                sadalp v21.4s, v13.8h
                sadalp v22.4s, v14.8h
                sadalp v23.4s, v15.8h

                subs x13, x13, #1
                beq LoopIcEnd

            LoopIcEnd:
                smull v8.8h, v4.8b, v2.8b
                smull v12.8h, v4.8b, v3.8b
                smull2 v9.8h, v4.16b, v2.16b
                smull2 v13.8h, v4.16b, v3.16b
                smull v10.8h, v5.8b, v2.8b
                smull v14.8h, v5.8b, v3.8b
                smull2 v11.8h, v5.16b, v2.16b
                smull2 v15.8h, v5.16b, v3.16b
                sadalp v24.4s, v8.8h
                sadalp v25.4s, v9.8h
                sadalp v26.4s, v10.8h
                sadalp v27.4s, v11.8h
                sadalp v28.4s, v12.8h
                sadalp v29.4s, v13.8h
                sadalp v30.4s, v14.8h
                sadalp v31.4s, v15.8h

                addp v16.4s, v16.4s, v17.4s
                addp v18.4s, v18.4s, v19.4s
                addp v16.4s, v16.4s, v18.4s

                addp v20.4s, v20.4s, v21.4s
                addp v22.4s, v22.4s, v23.4s
                addp v20.4s, v20.4s, v22.4s

                addp v24.4s, v24.4s, v25.4s
                addp v26.4s, v26.4s, v27.4s
                addp v24.4s, v24.4s, v26.4s

                addp v28.4s, v28.4s, v29.4s
                addp v30.4s, v30.4s, v31.4s
                addp v28.4s, v28.4s, v30.4s

                ld1 {v7.4s}, [x3], #16
                sub v16.4s, v16.4s, v7.4s
                sub v20.4s, v20.4s, v7.4s
                sub v24.4s, v24.4s, v7.4s
                sub v28.4s, v28.4s, v7.4s
            Relu:
                dup v0.4s, w8
                fmax v16.4s, v16.4s ,v0.4s
                fmax v20.4s, v20.4s ,v0.4s
                fmax v24.4s, v24.4s ,v0.4s
                fmax v28.4s, v28.4s ,v0.4s

                dup v1.4s, w9
                fmin v16.4s, v16.4s ,v1.4s
                fmin v20.4s, v20.4s ,v1.4s
                fmin v24.4s, v24.4s ,v1.4s
                fmin v28.4s, v28.4s ,v1.4s

                // prefetching is not prefered while writing results in spite of cache missings
                // you could try prfm pstl2strm
            WriteStart:
                cmp x6, #1
                beq Write1
                cmp x6, #2
                beq Write2
                cmp x6, #3
                beq Write3
                b Write4
            Write1:
                b WriteEnd
            Write2:
                b WriteEnd
            Write3:
                b WriteEnd
            Write4:
                st1 {v16.4s}, [x11], x7
                st1 {v20.4s}, [x11], x7
                st1 {v24.4s}, [x11], x7
                st1 {v28.4s}, [x11]

        WriteEnd:

            subs x10, x10, #1
            add x0, x0, #16
            bne LoopKsize

        subs x6, x6, #1
        bgt LoopOc

    sub sp, sp, #128
    ld1 {v8.4s, v9.4s, v10.4s, v11.4s}, [sp], #64
    ld1 {v12.4s, v13.4s, v14.4s, v15.4s}, [sp], #64
    ret
#endif